package operation

import (
	"context"
	"errors"
	"fmt"
	"rdSql/rds"
	"rdSql/table"
	"rdSql/util"
	"reflect"
)

var (
	NotDefinedType = errors.New("not defined type")
)

type SelectBuilder struct {
}

func (s SelectBuilder) GetFields() []string {
	return nil
}

type SelectOperation struct {
	whereKey string
	Err      error
}

func (s *SelectOperation) Where(rTable table.RTable) *SelectOperation {
	if rTable != nil {
		selectKey, err := table.GetRTableSelectKey(rTable)
		if err != nil {
			s.Err = err
			fmt.Println(err)
			return s
		}
		s.whereKey = selectKey
	}
	return s
}

func (s *SelectOperation) WhereMap(rTable table.RTable, whereArgs map[string]interface{}) *SelectOperation {
	if rTable != nil && whereArgs != nil {
		selectKey, err := table.GetSelectKey(rTable, whereArgs)
		if err != nil {
			s.Err = err
			fmt.Println(err)
			return s
		}
		s.whereKey = selectKey
	}
	return s
}

var SelectLengthNotMatch = errors.New("select fields length is not match result length.")
var ExceptNotMatch = errors.New("except one bug get ")

func (s *SelectOperation) FindOne(rTable table.RTable, selectArgs ...interface{}) error {
	// 如果没有where条件，尝试使用rTable作为where
	if s.whereKey == "" {
		s.Where(rTable)
	}
	var selectFields []string
	if selectArgs != nil && len(selectArgs) > 0 {
		if len(selectArgs) == 1 {
			switch selectArgs[0].(type) {
			case SelectBuilder:
				selectFields = selectArgs[0].(SelectBuilder).GetFields()
			case *SelectBuilder:
				selectFields = selectArgs[0].(SelectBuilder).GetFields()
			case []string:
				selectFields = selectArgs[0].([]string)
			case string:
				selectFields = append(selectFields, selectArgs[0].(string))
			default:
				return NotDefinedType
			}
		} else {
			for _, selectArg := range selectArgs {
				if selectStr, ok := selectArg.(string); ok {
					selectFields = append(selectFields, selectStr)
				}
			}
		}
	}
	var err error
	var rdsGetMap = make(map[string]string)
	result, _, err := rds.RdsClient.Scan(context.Background(), 0, s.whereKey, 10).Result()
	if err != nil {
		return err
	}
	if len(result) != 1 {
		return ExceptNotMatch
	}
	if selectFields == nil || len(selectFields) == 0 {
		rdsGetMap, err = rds.RdsClient.HGetAll(context.Background(), result[0]).Result()
		if err != nil {
			return err
		}
		fmt.Println("hGetAll:", rdsGetMap)
	} else {
		rdsGet, err := rds.RdsClient.HMGet(context.Background(), result[0], selectFields...).Result()
		if err != nil {
			return err
		}
		fmt.Println(rdsGet)
		if len(selectFields) != len(rdsGet) {
			return SelectLengthNotMatch
		}
		for i, rdsGetVal := range rdsGet {
			rdsGetMap[selectFields[i]] = util.BeString(rdsGetVal)
		}
	}
	return StrMapToPtr(rTable, rdsGetMap)
}

func StrMapToPtr(res table.RTable, mapStr map[string]string) error {
	values := reflect.ValueOf(res)
	if values.Kind() != reflect.Ptr || values.IsNil() {
		return nil
	}
	mapInterface := make(map[string]interface{})
	for mapStrKey, mapStrVal := range mapStr {
		mapInterface[mapStrKey] = mapStrVal
	}
	values = values.Elem()
	types := reflect.TypeOf(res).Elem()
	for i := 0; i < values.NumField(); i++ {
		fieldName := firstCharLower(types.Field(i).Name)
		if val := mapInterface[fieldName]; val != nil {
			strTo, err := StrTo(types.Field(i).Type.Kind(), val.(string))
			if err != nil {
				return errors.New(err.Error() + ",fieldName:" + fieldName)
			}
			values.Field(i).Set(reflect.ValueOf(strTo))
		}
	}
	return nil
}
func firstCharUpper(upperChar string) string {
	upRunes := []rune(upperChar)
	if upRunes == nil || len(upRunes) == 0 {
		return ""
	}
	if upRunes[0] >= 97 && upRunes[0] <= 122 {
		upRunes[0] = upRunes[0] - 32
	}
	return string(upRunes)
}

func firstCharLower(upperChar string) string {
	upRunes := []rune(upperChar)
	if upRunes == nil || len(upRunes) == 0 {
		return ""
	}
	if upRunes[0] >= 65 && upRunes[0] <= 90 {
		upRunes[0] = upRunes[0] + 32
	}
	return string(upRunes)
}

var NotMatchType = errors.New("not match type")

func StrTo(kind reflect.Kind, transPar string) (interface{}, error) {
	switch kind {
	case reflect.String:
		return transPar, nil
	case reflect.Int:
		return util.Str2IntNoPanic(transPar), nil
	case reflect.Int8:
		return util.Str2Int8NoPanic(transPar), nil
	case reflect.Int16:
		return util.Str2Int16NoPanic(transPar), nil
	case reflect.Int32:
		return util.Str2Int32(transPar), nil
	case reflect.Int64:
		return util.Str2Int64(transPar), nil
	case reflect.Uint8:
		return util.Str2Uint8NoPanic(transPar), nil
	case reflect.Uint16:
		return util.Str2Uint16NoPanic(transPar), nil
	case reflect.Uint32:
		return util.Str2Uint32NoPanic(transPar), nil
	case reflect.Uint64:
		return util.Str2Uint64NoPanic(transPar), nil
	case reflect.Float32:
		return util.Str2Float32(transPar), nil
	case reflect.Float64:
		return util.Str2Float64NoPanic(transPar), nil
	case reflect.Bool:
		return util.Str2BoolNoPanic(transPar), nil
	case reflect.Slice:
		return nil, nil
	default:
		return nil, NotMatchType
	}
}
